
import rescuecore.*;
import rescuecore.objects.*;
import rescuecore.event.*;
import java.util.*;

public class Robocop extends PlatoonAgent {
	private int commId;
	private static int nextCommId=0;
	private String messageToTell=""; 

    /**
    Lista de targets (Buildings em incêndio percebidos pelo agente)
     */
    private List targets;
    private List specialTargets;
    private List teamTargets;
	private List visited;
	private List storedBuildings;


    /**
    Constroi um PoliceForce Agent
    Nota: observe o parâmetro passado ao construtor da superclasse, que determina o TIPO do agente (PoliceForce)
     */
    public Robocop() {
        super(RescueConstants.AGENT_TYPE_POLICE_FORCE);
        targets = new ArrayList();
        specialTargets = new ArrayList();
        teamTargets = new ArrayList();
        visited = new ArrayList();
		storedBuildings = new ArrayList();
    }

    /**
    Retorna uma referência de mim mesmo no mundo (necessário pelo simulador)       
    @return the PoliceForce controlled by this agent
     */
    private PoliceForce me() {
        return (PoliceForce) memory.lookup(id);
    }

    /**
     * Inicialização do agente:
    Nesta inicialização é criado um Listener ("callback") que será notificado sempre
    que um novo objeto do tipo Building for adicionado ou modificado na memória (percepção) do agente. 
    Este Listener tem as seguintes funções:
    1. Se o Building ADICIONADO (percebido) está incendiado, adiciona nos targets a serem atacados 
    2. Se um dos Buildings que já estavam na memória tiverem alteração na propriedade FIERYNESS 
    ("grau do incêndio"). Caso a alteração seja indicando que o Building começou a incendiar, 
    adicina nos targets. Caso contrário (já não está mais incendiando), remove dos targets
     */
    public void initialise(RescueObject[] knowledge, RescueObject self) {
        super.initialise(knowledge, self);

		commId = ++nextCommId;
		//System.out.println("Robocop commId: "	+ commId );

        memory.addMemoryListener(new MemoryListener() {

            public void objectAdded(ObjectAddedEvent event) {
                RescueObject o = event.getObject();
                if (o.isRoad()) {
					if (((Road) o).isBlocked() && !((Road) o).hasFreeLines()) {
                        if(!targets.contains(o)){
							targets.add(o);
						}
						messageToTell = new String (messageToTell + "R=" + ((Road)o).getID() + ";");
					}
                }
				else if (o.isHumanoid()) {
					Humanoid h = (Humanoid)o;
					if ((h.isBuried() || h.isDamaged()) && h.isAlive() && memory.lookup(h.getPosition())!=null) {
						messageToTell = new String ("V=" + h.getPosition() + ";" + messageToTell);
					}
				}
				else if (o.isBuilding()) {
					if (((Building)o).isOnFire()) {
						if(!storedBuildings.contains(o)){
							storedBuildings.add(o);
						}
						messageToTell = new String (messageToTell + "B=" + ((Building) o).getID() + ";") ;
					}
				}
            }

            public void objectChanged(ObjectChangedEvent event) {
                RescueObject o = event.getObject();
                if (o.isRoad() && event.getProperty() == RescueConstants.PROPERTY_BLOCK) {
                    if (((Road) o).isBlocked()) {
                        if(!targets.contains(o)){
							targets.add(o);
							messageToTell = new String (messageToTell + "R=" + ((Road)o).getID() + ";");
						}
                    } else {
                        targets.remove(o);
						teamTargets.remove(o);
                    }
                }
            }
        });
    }

    public void sense() {
		boolean wasSomethingDecided = false;

        RescueObject location = getLocation();
        if (location.isRoad() && ((Road) location).isBlocked()) {
            clear((Road) location);
			specialTargets.remove(location);
			teamTargets.remove(location);
            wasSomethingDecided = true; 
        }

        // Ordenar os targets por proximidade
        if(!wasSomethingDecided && !specialTargets.isEmpty()) SampleSearch.sortByDistance(specialTargets, me(), memory);


        // Remover Bloqueios dos Time 
        for (Iterator it = specialTargets.iterator(); it.hasNext() && !wasSomethingDecided;) {
            Road next = (Road) it.next();
            if (!next.isBlocked()){ 
                // Se nao bloqueado remove da lista
                it.remove();
                continue;
            }
			else if(moveToNextTarget(next)){
				wasSomethingDecided = true;
			}
        }

        // Ordenar os targets por proximidade
        if(!wasSomethingDecided && !teamTargets.isEmpty()) SampleSearch.sortByDistance(teamTargets, me(), memory);


        // Remover Bloqueios dos Time 
        for (Iterator it = teamTargets.iterator(); it.hasNext() && !wasSomethingDecided;) {
            Road next = (Road) it.next();
            if (!next.isBlocked()){ 
                // Se nao bloqueado remove da lista
                it.remove();
                continue;
            }
			else if(moveToNextTarget(next)){
				wasSomethingDecided = true;
			}
        }


        if(!wasSomethingDecided && !targets.isEmpty()) SampleSearch.sortByDistance(targets, me(), memory);

        // Remover bloqueios mais proximos
        for (Iterator it = targets.iterator(); it.hasNext() && !wasSomethingDecided;) {
            Road next = (Road) it.next();
            if (!next.isBlocked()){ 
                // Se nao bloqueado remove da lista
                it.remove();
                continue;
            }
			else if(moveToNextTarget(next)){
				wasSomethingDecided = true;
			}
        }

		if(timeStep % nextCommId == (commId-1) && messageToTell.length() > 0){
			tell(messageToTell.getBytes());
			//System.out.println( me() + " at step " + timeStep + ", telling: " + messageToTell);
			messageToTell = "";
		}

		if(!wasSomethingDecided) moveToRandomTarget();

    }

	private boolean moveToNextTarget(Road r) {
		int[] path = SampleSearch.breadthFirstSearch(getLocation(), r, memory); //procura proximo

		if (path != null) {                
			//se nao achar procura o proximo 
			move(path);
			return true;
		}

		return false;
	}

	private boolean moveToRandomTarget() {
        // Fica procurando randomicamente 
        Collection<RescueObject> allRoads = memory.getObjectsOfType(RescueConstants.TYPE_ROAD); // Find all roads

        Road[] roads = new Road[allRoads.size()];
        allRoads.toArray(roads);
		Road target = (Road) roads[(int) (Math.random() * roads.length)]; 


        int[] path = SampleSearch.breadthFirstSearch(getLocation(), target, memory);
        if (path != null) {
            move(path);
			return true;
        } 
		return false;
	}


	public boolean willListenHearTell(int from) {
		if(from == id) return false;
		else return true;
	}
	

	public boolean willListenHearSay(int from) {
		if(from == id) return false;
		else return true;
	}


	protected void parserRoad(String msg){
		String [] result = msg.split(";");
		String received = new String();

		for (int i=0; i<result.length; i++){
			if(result[i].split("=")[0].startsWith("R")) {
				Object o = memory.lookup( (new  Integer(result[i].split("=")[1])).intValue() );
				if(!teamTargets.contains((Road) o)){
					teamTargets.add((Road) o);
					received = received + result[i].split("=")[1] + ";";
				}
			}
			else if(result[i].split("=")[0].startsWith("S")) {
				Object o = memory.lookup( (new  Integer(result[i].split("=")[1])).intValue() );
				if(!teamTargets.contains((Road) o)){
					specialTargets.add((Road) o);
					received = received + result[i].split("=")[1] + ";";
				}
			}
		}
		
		if(received.length() > 0) System.out.println("[" + me() + "] Team Tasks received: " + received );
	}


	protected boolean hearTell(int from, byte[] msg){
		if(from != id ) parserRoad(new String(msg));
		//System.out.println("[" + me() + "] "  + new String(msg) );
		return true;
	}

	protected boolean hearSay(int from, byte [] msg){
		//System.out.println("Robocop escutou " + new String(msg) + " de " + from);
		return true;
	}
   
}
